/*
 *  Created at 2011-02-15 19:40 GMT-03:00
 *  Author: Victor Williams Stafusa da Silva
 *  JSmartUnit
 */
package org.jsmartunit.runners;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jsmartunit.invocations.ClassInvocation;
import org.jsmartunit.invocations.SingleInstanceAllMethodsInvocation;
import org.jsmartunit.model.InstanceFactory;
import org.jsmartunit.model.TestRunner;
import org.jsmartunit.util.CollectionUtils;

import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;

/**
 * @author Victor Williams Stafusa da Silva
 */
public final class MethodInvokerFactory {

    private final RunNotifier notifier;
    private final String prefix;

    public MethodInvokerFactory(String prefix, RunNotifier notifier) {
        if (prefix == null) throw new IllegalArgumentException();
        if (notifier == null) throw new IllegalArgumentException();

        this.prefix = prefix;
        this.notifier = notifier;
    }

    public List<TestRunner> runnersFor(Object target) throws InitializationError {
        //if (target instanceof MethodInvocation) return ((MethodInvocation) target).createRunner(parent, notifier);
        //if (target instanceof ConstructorInvocation<?>) return ((ConstructorInvocation<?>) target).createRunner(parentRunner, name, notifier);
        if (target instanceof Map<?, ?>) return runnerFor((Map<?, ?>) target);
        if (target instanceof Iterable<?>) return runnerFor((Iterable<?>) target);
        if (target instanceof Iterator<?>) return runnerFor((Iterator<?>) target);
        if (target instanceof Enumeration<?>) return runnerFor((Enumeration<?>) target);
        if (target instanceof Object[]) return runnerFor((Object[]) target);
        if (target instanceof Class<?>) return Collections.singletonList(new ClassInvocation(prefix, notifier, (Class<?>) target).createRunner());
        if (target instanceof Object) return Collections.singletonList(new SingleInstanceAllMethodsInvocation(prefix, notifier, InstanceFactory.Factories.box(target)).createRunner());
        return null;
        //return new MethodInvoker(method, parentRunner, name, parameters);
    }

    public List<TestRunner> runnerFor(Iterable<?> iterable) throws InitializationError {
        return runnerFor(CollectionUtils.toMap(iterable));
    }

    public List<TestRunner> runnerFor(Iterator<?> iterator) throws InitializationError {
        return runnerFor(CollectionUtils.toMap(iterator));
    }

    public List<TestRunner> runnerFor(Enumeration<?> enumeration) throws InitializationError {
        return runnerFor(CollectionUtils.toMap(enumeration));
    }

    public List<TestRunner> runnerFor(Object[] array) throws InitializationError {
        return runnerFor(CollectionUtils.toMap(array));
    }

    public List<TestRunner> runnerFor(Map<?, ?> map) throws InitializationError {
        List<TestRunner> runners = new ArrayList<TestRunner>(map.size());

        for (Map.Entry<?, ?> entry : map.entrySet()) {
            Object key = entry.getKey();
            Object value = entry.getValue();
            if (value != null) {
                TestRunner inner = new TestRunner(prefix + "-index[" + key + "]");
                inner.addChildren(new MethodInvokerFactory(prefix + "-index[" + key + "]", notifier).runnersFor(value));
                inner.setStatement(inner.getChildrenRunnerStatement());
                runners.add(inner);
            }
        }

        return runners;
    }
}
